#include "DataCell.h"
#include "scImpl.h"

#define FCT "DataCell::DataCell(t)"
DataCell::DataCell(Type type) : VarC((VarC::Type)type)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(0);
}

#undef FCT
#define FCT "DataCell::DataCell(ctor)"
DataCell::DataCell(const DataCell & that): VarC(that)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(&that);
}

#undef FCT
#define FCT "DataCell::DataCell(ti)"
DataCell::DataCell(Type type, const char * id) : VarC((VarC::Type)type, id)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(0);
}

#undef FCT
#define FCT "DataCell::~DataCell"
DataCell::~DataCell()
{
	release();
}

#undef FCT
#define FCT "DataCell::operator="
DataCell & DataCell::operator=(const DataCell & that)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
	bool morphed(ty!=that.ty);
	assign(that); // that's from VarC
	if(morphed)
		create(&that); // that's from DataCell
	else
	{
		if((DataCell::Type)ty==TY_DBKEY)
			*((PkType *)(d.dp))=*((PkType *)(that.d.dp));
		else if((DataCell::Type)ty==TY_QSTRING)
			*((QString *)(d.dp))=*((QString *)(that.d.dp));
		else if((DataCell::Type)ty==TY_QDATETIME)
			*((QDateTime *)(d.dp))=*((QDateTime *)(that.d.dp));
	}
	return *this;
}

#undef FCT
#define FCT "DataCell::DataCell(db)"
DataCell::DataCell(const PkType & data, const char * id, const char * visibleName, const char * unit) :
	VarC((VarC::Type)TY_DBKEY, id)
{
	ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(0);
	setName(std::string(visibleName));
	setUnit(std::string(unit));
	operator=(data);
	clearDirty();
}

#undef FCT
#define FCT "DataCell::DataCell(QS)"
DataCell::DataCell(const QString & data, const char * id, const char * visibleName, const char * unit) :
	VarC((VarC::Type)TY_QSTRING, id)
{
	ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(0);
	setName(std::string(visibleName));
	setUnit(std::string(unit));
	operator=(data);
	clearDirty();
}

#undef FCT
#define FCT "DataCell::DataCell(QD)"
DataCell::DataCell(const QDateTime & data, const char * id, const char * visibleName, const char * unit) :
	VarC((VarC::Type)TY_QDATETIME, id)
{
	//ctrc(Trc::LOOP,APP,FCT)<<endl;
	create(0);
	setName(std::string(visibleName));
	setUnit(std::string(unit));
	operator=(data);
	clearDirty();
}

#undef FCT
#define FCT "DataCell::tryUpdate"
bool DataCell::tryUpdate(QString data, bool asUser)
{
	bool res(false);
	switch((DataCell::Type)ty)
	{
	case TY_BOOL:
	{
		bool bdata(data.toLower()=="true" || data=="1");
		bool success(bdata || data.toLower()=="false" || data=="0");
		if(success && d.db!=bdata)
		{
			d.db=bdata;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_INT:
	{
		bool success;
		long idata(data.toLong(&success));
		if(success && d.di!=idata)
		{
			d.di=idata;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_DOUBLE:
	{
		bool success;
		double ddata(data.toDouble(&success));
		if(success && d.db!=ddata)
		{
			d.dd=ddata;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_STRING:
	{
		if(*((std::string *)d.dp)!=data.toStdString())
		{
			*((std::string *)d.dp)=data.toStdString();
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_DYNENUM:
	{
		int enumCode(((DynEnum*)d.dp)->translate(data.toStdString()));
		if(enumCode>=0 && ((DynEnum*)d.dp)->getCode()!=enumCode)
		{
			*((DynEnum*)d.dp)=enumCode;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_DBKEY:
	{
		bool success;
		PkType idata(data.toLongLong(&success));
		if(success && *((PkType*)d.dp)!=idata)
		{
			*((PkType*)d.dp)=idata;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_QSTRING:
	{
		if(*((QString*)d.dp)!=data)
		{
			*((QString*)d.dp)=data;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	case TY_QDATETIME:
	{
		QDateTime tst(QDateTime::fromString(data, QString(asUser?DATETIME_USER:DATETIME_TECH)));
		if(!asUser)
			tst.setTimeSpec(Qt::UTC); // all timestampps from the server are UTC-based
		//ctrc(Trc::LOOP,APP,FCT)<<"decoded to "<<tst.toString(QString("d.MMM.yy hh:mm")).toStdString().c_str()<<endl;
		if(tst.isValid() && *((QDateTime*)d.dp)!=tst)
		{
			*((QDateTime*)d.dp)=tst;
			state=ST_DIRTY;
			res=true;
		}
		break;
	}
	default:
		break;
	}
	//if(!res)
	//	ctrc(Trc::WORK,APP,FCT)<<"update failed with data "<<data<<" for type "<<ty<<endl;
	return res;
}

#undef FCT
#define FCT "DataCell::getAsInt"
long DataCell::getAsInt() const
{
	switch(ty)
	{
	case TY_BOOL:
		return d.db?1:0;
	case TY_INT:
		return d.di;
	case TY_DOUBLE:
		return (long)d.dd;
	case TY_STRING:
	{
		long res(0);
		istringstream is(*((std::string *)d.dp));
		is >>res;
		return res;
	}
	case TY_DYNENUM:
		return ((DynEnum*)d.dp)->getCode();
	case TY_DBKEY:
		return (long)(*((PkType*)d.dp));
	case TY_QSTRING:
		return ((QString*)d.dp)->toLong();
	case TY_QDATETIME:
		return ((QDateTime*)d.dp)->toTime_t();
	default:
		return 0;
	}
}

#undef FCT
#define FCT "DataCell::getAsQString"
QString DataCell::getAsQString(bool asUser) const
{
	switch(ty)
	{
	case TY_BOOL:
		return d.db?QString("true"):QString("false");
	case TY_INT:
		return QString::number(d.di);
	case TY_DOUBLE:
		return QString::number(d.dd);
	case TY_STRING:
		return QString::fromStdString(*((std::string *)d.dp));
	case TY_DYNENUM:
		return QString::fromStdString(((DynEnum*)d.dp)->getValue());
	case TY_DBKEY:
		return QString::number(*((PkType*)d.dp));
	case TY_QSTRING:
		return *((QString*)d.dp);
	case TY_QDATETIME:
		if(asUser)
			return ((QDateTime*)d.dp)->toString(QString(DATETIME_USER));
		else
			return ((QDateTime*)d.dp)->toString(QString(DATETIME_TECH));
	default:
		return 0;
	}
}

#undef FCT
#define FCT "DataCell::create"
void DataCell::create(const DataCell * that)
{
	//ctrc(Trc::WORK,APP,FCT)<<"anonymous"<<endl;
	switch((DataCell::Type)ty)
	{
	case TY_DBKEY:
		if(that)
			d.dp=new PkType(*((PkType*)that->d.dp));
		else
			d.dp=new PkType();
		break;
	case TY_QSTRING:
		if(that)
			d.dp=new QString(*((QString*)that->d.dp));
		else
			d.dp=new QString();
		break;
	case TY_QDATETIME:
		if(that)
			d.dp=new QDateTime(*((QDateTime*)that->d.dp));
		else
			d.dp=new QDateTime();
		break;
	default:
		; // ctrc(Trc::INNERLOOP,APP,FCT)<<" d.dp to be created by derived class"<<endl;
	}
}

#undef FCT
#define FCT "DataCell::release"
void DataCell::release()
{
	switch((DataCell::Type)ty)
	{
	case TY_DBKEY:
		delete (PkType *)(d.dp);
		break;
	case TY_QSTRING:
		delete (QString *)(d.dp);
		break;
	case TY_QDATETIME:
		delete (QDateTime *)(d.dp);
		break;
	default:
		VarC::release(); //ctrc(Trc::INNERLOOP,APP,FCT)<<" d.dp to be deleted by derived class"<<endl;
	}
}

#undef FCT
#define FCT "std::operator<<(QString)"
ostream& std::operator<<(ostream& out, const ::QString & q)
{
	out<<"QString("<<q.toStdString().c_str()<<')';
	return out;
}

#undef FCT
#define FCT "std::operator<<(QDateTime)"
ostream& std::operator<<(ostream& out, const ::QDateTime & q)
{
	out<<"QDateTime("<<q.toString().toStdString().c_str()<<"|"<<q.timeSpec()<<')';
	return out;
}

#undef FCT
#define FCT "std::operator<<(DataCell)"
ostream& std::operator<<(ostream& out, const sybilpm::DataCell & var)
{

	sybilpm::DataCell::Type ty((sybilpm::DataCell::Type)var.getType());
	if(ty==sybilpm::DataCell::TY_DBKEY)
		out<<"/d:"<<var.getDbKey();
	else if(ty==sybilpm::DataCell::TY_QSTRING)
		out<<"/d:"<<var.getQString();
	else if(ty==sybilpm::DataCell::TY_QDATETIME)
		out<<"/d:"<<var.getQDateTime();
	return operator<<(out, (const dwkUtil::VarC &)var);
}
